/** 对axios做一些配置
 * 参考”前端大全”公众号文章
 * GET POST类请求ＵＲＬ末尾一律要加/，否则浏览器会报ＣＯＲＳ类的错，其实后台配置没问题。
 **/
import axios, { AxiosError } from "axios";
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
import qs from "qs";
import { jwtDecode } from "jwt-decode";

import { store } from "../store/configureStore";
import { setUserAction } from "@/store/actions";

//
export interface RequestInterceptors {
  requestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfig;
  requestInterceptorsCatch?: (err: unknown) => unknown;

  responseInterceptors?: <T = AxiosResponse>(config: T) => T;
  responseInterceptorsCatch?: (err: unknown) => unknown;
}

// 自定义传入的参数
export interface RequestConfig extends AxiosRequestConfig {
  interceptors?: RequestInterceptors;
}

interface CustomRequestConfig<T> extends RequestConfig {
  data?: T;
}

export interface CustomResponse<T> {
  code: number;
  message: string;
  data: T;
  [keys: string]: unknown;
}

// 防抖通过ahooks的useRequest实现了
export interface CancelRequestSource {
  // 这里的key是不固定的，因为我们使用url做index，只有用的时候才知道url，使用此语法解决
  [index: string]: (value: unknown) => void;
}

let isRefreshing = false;
let failedQueue: any[] = [];

const processQueue = (error: any, token = null) => {
  failedQueue.forEach((prom) => {
    if (error) {
      prom.reject(error);
    } else {
      prom.resolve(token);
    }
  });

  failedQueue = [];
};

function isTokenExpired(token: string) {
  try {
    const decoded = jwtDecode(token);
    // console.log(decoded);
    const currentTime = Date.now() / 1000;
    if (decoded.exp!) {
      return false;
    }
    if (decoded.exp! < currentTime) {
      return true;
    }
    return false;
  } catch (err) {
    return false;
  }
}

async function handleTokenRefresh(config: RequestConfig) {
  console.log(config);
  isTokenExpired(store.getState()?.user?.accessToken!);
  return new Promise(async (resolve, reject) => {
    if (!isRefreshing) {
      isRefreshing = true;

      try {
        const response = await axios.post(`${config.baseURL}token-refresh/`, {
          refresh_token: localStorage.getItem("refresh_token"),
        });
        const { access_token, refresh_token } = response.data;

        localStorage.setItem("access_token", access_token);
        localStorage.setItem("refresh_token", refresh_token);

        resolve(config); // 重新发送原始请求
      } catch (error) {
        processQueue(error, null);
        reject(error);
      } finally {
        isRefreshing = false;
      }
    } else {
      // 将当前请求加入队列，等待 token 刷新完成后再执行
      return new Promise((resolve, reject) => {
        failedQueue.push({ resolve, reject });
      });
    }
  });
}

// 封装成一个类而不是一个函数，因为类可以创建多个实例，适用范围更广，封装性更强
// 拦截器有三种：类拦截器、实例拦截器、接口拦截器
// 拦截器执行顺序：实例请求 --> 类请求 --> 实例响应 --> 类响应
class Request {
  instance: AxiosInstance;

  /*
   * 存放取消方法的集合
   * 在创建请求后将取消请求方法push到该集合中
   * 封装一个方法可以取消请求，传入url: string | string[]
   * 在请求之前判断同一URL是否存在，如果存在就取消请求
   */
  cancelRequestSourceList?: CancelRequestSource[];

  /*
   * 存放所有请求URL的集合
   * 请求之前需要将URL push到该集合中
   * 请求完毕后将url从集合中删除
   * 添加在发送请求之前完成，删除在响应之后删除
   */
  requestURLList?: string[];

  interceptorsObj?: RequestInterceptors;

  constructor(config: RequestConfig) {
    this.instance = axios.create(config);

    this.requestURLList = [];
    this.cancelRequestSourceList = [];

    // 类拦截器
    this.instance.interceptors.request.use(
      // (config: AxiosRequestConfig) => {
      (config: any) => {
        // console.log("全局请求拦截器");
        // console.log(store.getState().user.accessToken);
        // 对于大多数场景，推荐将 access_token 存储在带有 HttpOnly 和 Secure 标志的 cookie 中，
        // 以最大限度地减少 XSS 和 CSRF 攻击的风险。同时，可以根据具体情况决定是否将 refresh_token
        // 存储在 localStorage 或其他地方，但要注意加强对其的保护措施。此外，始终遵循最佳实践，
        // 比如定期轮换 token、设置合理的有效期等。
        if (store.getState()?.user?.accessToken && config && config.headers) {
          isTokenExpired(store.getState()?.user?.accessToken!);
          config.headers.Authorization =
            "Bearer " + store.getState()?.user?.accessToken;
        } else {
          // console.log("no token");
        }
        if (config.method?.toUpperCase() === "POST" && config.headers) {
          // 设置发送的数据类型
          // config.headers["Content-Type"] = "application/x-www-form-urlencoded";
          // 优先用实例中定义好的Conteent-Type值
          if (!config.headers["Content-Type"]) {
            config.headers["Content-Type"] = "application/json";
          }
        }

        if (config.method?.toUpperCase() === "GET") {
          config.paramsSerializer = function (params: unknown) {
            return qs.stringify(params, { arrayFormat: "repeat" });
          };
        }
        return config;
      },
      (err: unknown) => err
    );

    // this.instance.interceptors.request.use(
    //   (config: any) => {
    //     // 在发送请求之前做一些事情
    //     const accessToken = localStorage.getItem("access_token");
    //     if (accessToken) {
    //       config.headers.Authorization = `Bearer ${accessToken}`;
    //     }

    //     // 如果 access_token 无效，尝试刷新 token
    //     if (!accessToken || isTokenExpired(accessToken)) {
    //       return handleTokenRefresh(config);
    //     }

    //     return config;
    //   },
    //   (error) => {
    //     return Promise.reject(error);
    //   }
    // );

    // 使用实例拦截器
    this.instance.interceptors.request.use(
      this.interceptorsObj?.requestInterceptors as any,
      this.interceptorsObj?.requestInterceptorsCatch
    );

    this.instance.interceptors.response.use(
      this.interceptorsObj?.responseInterceptors,
      this.interceptorsObj?.responseInterceptorsCatch
    );

    // 全局响应拦截器保证最后执行
    this.instance.interceptors.response.use(
      (res: AxiosResponse) => {
        // console.log("全局响应拦截器");
        return res;
      },
      (error: AxiosError) => {
        if (error && error.response && error.response.status) {
          switch (error.response.status) {
            case 400:
              return Promise.reject(error.response.data);
            case 401:
              // 清除本地无效的token
              // localStorage.removeItem("access_token");
              // localStorage.removeItem("refresh_token");
              store.dispatch(setUserAction({ accessToken: undefined }));
              // 跳转到登录页面
              window.location.href = "/login";
              return Promise.reject(error.response.data);
            case 403: //
              return Promise.reject(error.response.data);
            case 404:
              return Promise.reject(error.response);
            case 500:
              return Promise.reject(error.response);
            default:
              return Promise.reject(error.response);
          }
        } else {
          if (!window.navigator.onLine) {
            // 断网处理：可以跳转到断网页面
            return Promise.reject(error.response);
          }
          // 服务器无响应又没断网，返回报错
          // message.error("网络错误");
          // store.dispatch(push("/error"));
          console.log(error);
          return Promise.reject(error);
        }
      }
    );
  }

  request<T>(config: RequestConfig): Promise<T> {
    return new Promise((resolve, reject) => {
      // 如果我们为单个请求设置拦截器，这里使用单个请求的拦截器
      if (config.interceptors?.requestInterceptors) {
        config = config.interceptors.requestInterceptors(config);
      }

      // const url = config.url;

      this.instance
        .request<T>(config)
        .then((res: any) => {
          // 如果我们为单个响应设置拦截器，在这里使用单个响应的拦截器
          if (config.interceptors?.responseInterceptors) {
            res = config.interceptors.responseInterceptors<T>(res);
          }

          resolve(res);
        })
        .catch((err: AxiosError) => {
          reject(err);
        })
        .finally(() => {
          // console.log(url);
        });
    });
  }
}

let defaultsBaseURL = "";
switch (process.env.NODE_ENV) {
  case "development":
    defaultsBaseURL = "http://127.0.0.1:8001/api/v1/";
    break;
  case "production":
    defaultsBaseURL = "/api/v1/";
    break;
  default:
    defaultsBaseURL = "/api/v1/";
    break;
}

export const baseUrl = defaultsBaseURL;

const request = new Request({
  baseURL: defaultsBaseURL,
  timeout: 1000 * 60 * 5,
  validateStatus: (status: number): boolean => {
    return /^(2|3)\d{2}$/.test(status.toString());
  },
  // 请求是否带上cookie，此值居然影响ＣＯＲＳ响应，还有ＵＲＬ末尾的/
  // The value of the 'Access-Control-Allow-Origin' header in the response
  // must not be the wildcard '*' when the request's credentials mode is 'include'.
  // The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
  withCredentials: false,
  interceptors: {
    // 请求拦截器
    requestInterceptors: (config) => {
      console.log("实例请求拦截器");
      return config;
    },
    // 响应拦截器
    responseInterceptors: (result) => {
      console.log("实例响应拦截器");
      return result;
    },
  },
});

/*
 * @interface D 请求参数的interface
 * @interface T 响应结构的interface
 * @param {CustomRequestConfig} config 不管是GET还是POST请求都使用data
 * @return {Promise}
 */
const customRequest = <D, T = unknown>(config: CustomRequestConfig<D>) => {
  const { method = "GET" } = config;
  if (method.toUpperCase() === "GET") {
    config.params = config.data;
  }

  return request.request<CustomResponse<T>>(config);
};

export default customRequest;
